# JSON 数据格式
JSON(JavaScript Object Notation),JS 对象标记,数据交换格式
JSON 有两种结构
- 对象(object),表示一组无序的键值对,一个对象由 { } 括起来的,“名称/值”对之间使用 , 分隔,每个“名称”后跟一个 : ,格式例如
{"key1":value1, "key2":value2, "key3":value3, ...}
(注意:对象的属性名必须加双引号) - 数组(array),表示一组有序的值,一个数组由 [ ] 括起来,值之间使用 , 分隔,格式例如
[{"key1":value1,"key2":value2,"key3":value3}, {"key1":value1,"key2":value2,"key3":value3}, ...]
- 对象(object),表示一组无序的键值对,一个对象由 { } 括起来的,“名称/值”对之间使用 , 分隔,每个“名称”后跟一个 : ,格式例如
JSON 值可以是:数字(整数或浮点数)、字符串(在双引号中)、逻辑值(true 或 false)、数组(在方括号中)、对象(在花括号中)、null
{ "Number": 123, "String": "Hello World", "Boolean": true, "Array": [1, 2, 3], "Object": {"a": "b", "c": "d"}, "Null": null }
1
2
3
4
5
6
7
8Firefox 浏览器中将对象或数组转换为 JSON 字符串的方法:
对象或数组.toSource()
JSON 格式字符串转对象或数组的方法:
eval("(" + jsonString + ")")
,因为 JavaScript 规定,如果行首是花括号,一律解释为语句(即代码块),如果要解释为表达式(即对象),必须在花括号前加上圆括号
# Java 中操作 JSON 的库
# jackson (opens new window)
- 所需 jar 包:jackson-core.jar、jackson-annotations.jar、jackson-databind.jar (opens new window)
- JSON 与 Java 对象之间的转换:com.fasterxml.jackson.databind.ObjectMapper、com.fasterxml.jackson.databind.json.JsonMapper(jackson-databind.jar)
- XML 与 Java 对象之间的转换:com.fasterxml.jackson.dataformat.xml.XmlMapper(jackson-dataformat-xml.jar)
- YAML 与 Java 对象之间的转换:com.fasterxml.jackson.dataformat.yaml.YAMLMapper(jackson-dataformat-yaml.jar)
# jackson 中的处理 JSON 的三种方式
数据绑定:JSON 和 POJO 相互转换,基于属性访问器规约或注解(最常用)
树模型:提供一个 JSON 文档可变内存树的表示形式(最灵活)
- 容器节点抽象类 ContainerNode(extends BaseJsonNode(extends JsonNode))
- 子类:ObjectNode、ArrayNode
JsonNode jsonNode = mapper.readTree(jsonStr); ObjectNode objNode = mapper.createObjectNode(); // 创建对象节点 ArrayNode arrNode = mapper.createArrayNode(); // 创建数组节点 ArrayNode arrNode = objNode.withArray("propertyName")
1
2
3
4流式 API:读取和写入 JSON 内容作为离散事件(性能最佳:开销最低、速度最快的读/写;其它二者基于它实现),相关类:JsonParser、JsonGenerator
# JSON 与 Java 对象之间的转换
ObjectMapper 是线程安全的
在默认情况下,ObjectMapper 依赖于 Java 对象的默认的无参构造器进行反序列化,并且严格地通过 getter 和 setter 的命名规约进行序列化和反序列化
- jackson 反序列化时对象的属性类型不能为实例内部类,但可以为静态内部类(创建实例内部类对象前必须先创建外部类对象)
- 在序列化时,要求 field 可以被访问到,先通过 getter,如果没有再去找 field,如果还没有,就跳过这个 field
- 在反序列化时,通过反射,调用构造器,根据 json 里的 key-value 中的 key,去找对应变量的 setter,找不到就直接找对应变量,如果还找不到且没有设置 ignore unknown,就抛出异常
MapperFeature#USE_STD_BEAN_NAMING(false)
: Specific difference is that Jackson always lower cases leading upper-case letters, so "getURL()" becomes "url" property; whereas standard Bean naming only lower-cases the first letter if it is NOT followed by another upper-case letter (so "getURL()" would result in "URL" property).BeanUtil#legacyManglePropertyName
与BeanUtil#stdManglePropertyName
ObjectMapper 默认将 json 格式字符串中的对象结构转换为 LinkedHashMap 对象,数组结构转换为 ArrayList 对象
com.fasterxml.jackson.databind.ObjectMapper 类中的实例方法
String writeValueAsString(Object value)
:对象或集合转 json 格式字符串<T> T readValue(String content, Class<T> valueType)
:json 格式字符串转简单类型对象<T> T readValue(String content, TypeReference valueTypeRef)
:json 格式字符串转复杂类型对象(如有泛型类型字段的对象),如mapper.readValue(jsonStr, new TypeReference<Result<XXXX>>() {});
<T> T readValue(String content, JavaType valueType)
:json 格式字符串转复杂类型对象(如集合类型)<T> T convertValue(Object fromValue, Class<T> toValueType)
:将给定值转换为指定类型的对象通过 ObjectMapper 获取类型工厂 TypeFactory
mapper.getTypeFactory()
,再通过 TypeFactory 构造 JavaType(或其子类 ArrayType、CollectionType、MapType)JavaType constructType(TypeReference<?> typeRef)
JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses)
JavaType constructParametricType(Class<?> rawType, JavaType... parameterTypes)
ArrayType constructArrayType(Class<?> elementType)
ArrayType constructArrayType(JavaType elementType)
CollectionType constructCollectionType(Class<? extends Collection> collectionClass, Class<?> elementClass)
CollectionType constructCollectionType(Class<? extends Collection> collectionClass, JavaType elementType)
MapType constructMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass)
MapType constructMapType(Class<? extends Map> mapClass, JavaType keyType, JavaType valueType)
/** Concrete Java types that Jackson will use for simple data binding are: JSON Type Java Type object LinkedHashMap<String,Object> array ArrayList<Object> string String number(no fraction) Integer, Long or BigInteger (smallest applicable) number(fraction) Double(configurable to use BigDecimal) true|false Boolean null null */ JavaType javaType = mapper.getTypeFactory().constructParametricType(Result.class, User.class); Result<User> result = mapper.readValue(jsonStr, javaType); // List<User> userList = mapper.readValue(jsonStr, new TypeReference<Result<User>>() {}); ArrayType arrayType = mapper.getTypeFactory().constructArrayType(User.class); User[] users = mapper.readValue(jsonStr, arrayType); // User[] o = mapper.readValue(jsonStr, new TypeReference<User[]>() {}); CollectionType listType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, User.class); List<User> userList = mapper.readValue(jsonStr, listType); // List<User> userList = mapper.readValue(jsonStr, new TypeReference<List<User>>() {});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ObjectMapper setVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility)
:设置可见性ObjectMapper enableDefaultTyping(DefaultTyping dti)
:指定序列化时包含的属性类型信息(默认不开启,即不包含类型信息)ObjectMapper enableDefaultTyping(DefaultTyping applicability, JsonTypeInfo.As includeAs)
ObjectMapper configure(DeserializationFeature f, boolean state)
:打开/关闭某反序列化特性,如浮点数反序列化为 Double、忽略空属性等// 通过反射机制(而非 getter 和 setter 方法)直接操作对象上的字段 mapper.findAndRegisterModules() // 屏蔽所有 accessor .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE) // 任何字段可见 .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); // 格式化输出 // mapper.configure(MapperFeature.USE_STD_BEAN_NAMING, true); // 默认 false // mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); // 属性序列化顺序 mapper.disable(DeserializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 禁用序列化日期为 timestamps mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 禁用遇到未知属性抛出异常 mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); // {"@class":"org.apache.commons.lang3.tuple.MutableTriple","left":"1234","middle":["java.lang.Long",4562],"right":"triple"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# jackson 的多态类型处理 (opens new window)
方式一:设置全局的 DefaultTyping;方式二:使用 @JsonTypeInfo 注解
全局 DefaultTyping,ObjectMapper.DefaultTyping 枚举类中的枚举值:
- JAVA_LANG_OBJECT:序列化时包含属性类型为 Object 的类型信息
- OBJECT_AND_NON_CONCRETE:序列化时包含属性类型为 Object、非具体类型(抽象类和接口)的类型信息
- NON_CONCRETE_AND_ARRAYS:序列化时包含属性类型为 Object、非具体类型(抽象类和接口)以及数组元素类型的类型信息
- NON_FINAL:序列化时包含非 final 对象类型信息、以及属性中所有非 final 类型或者非 final 类型数组元素的类型信息
使用注解处理多态
@JsonTypeInfo:修饰类、字段、方法、参数,用于指出序列化包含的类型信息细节
- 属性:
- use:(必选)指定序列化类型信息时使用的类型识别码,属性值为 JsonTypeInfo.Id 中的枚举值:
- CLASS:使用完全限定类名做识别,此时默认的识别码属性名称 "@class"
- MINIMAL_CLASS:若基类和子类在同一包类,使用类名(忽略包名)作为识别码,此时默认的识别码属性名称 "@c"
- NAME:使用 @JsonSubTypes 或 @JsonTypeName 指定的逻辑类型名称,此时默认的识别码属性名称 "@type"
- CUSTOM:自定义识别码,需结合 property 属性和 @JsonTypeIdResolver
- NONE:不使用识别码,即序列化是不包含类型信息
- include:指定识别码是如何被包含进去的,属性值为 JsonTypeInfo.As 中的枚举值:
- PROPERTY:作为数据的属性(默认)
- EXISTING_PROPERTY:作为 POJO 中已经存在的属性
- EXTERNAL_PROPERTY:作为扩展属性
- WRAPPER_OBJECT:作为一个包装的对象
- WRAPPER_ARRAY:作为一个包装的数组
- property:指定识别码的属性名称
- defaultImpl:如果类型识别码不存在或者无效,指定反序列化时使用的默认类型
- visible:定义识别码在反序列化时是否保留,默认 false
- use:(必选)指定序列化类型信息时使用的类型识别码,属性值为 JsonTypeInfo.Id 中的枚举值:
- 属性:
@JsonSubTypes:用来指示该类的子类型以及逻辑类型名称
@JsonTypeName:用于定义类的逻辑类型名称
只有当 @JsonTypeInfo 的 use 属性值为 JsonTypeInfo.Id.NAME 时,才会使用逻辑类型名称
@Data public class Zoo { private Animal animal; }
1
2
3
4@Data @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true) @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat") }) public abstract class Animal { protected String name; protected String type; } @Data public class Dog extends Animal { private Double barkVolume; } @Data public class Cat extends Animal { private Boolean likesCream; private Integer lives; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21{ "animals": { "name": "lacy", "type": "cat", "likesCream": true, "lives": 5 } }
1
2
3
4
5
6
7
8// @JsonSubTypes 可以用其它方式代替 @Data @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true) public abstract class Animal { protected String name; protected String type; } @Data @JsonTypeName("dog") public class Dog extends Animal { private Double barkVolume; } @Data @JsonTypeName("cat") public class Cat extends Animal { private Boolean likesCream; private Integer lives; } mapper.registerSubtypes(Animal.class, Dog.class, Cat.class); // 注册子类或逻辑类型名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Jackson 的常用注解 (opens new window)
- @JsonInclude(JsonInclude.Include.NON_NULL),修饰类,序列化时忽略 null 属性,可配置
spring.jackson.default-property-inclusion=non_null
- @JsonIgnoreProperties(value = {"internalId", "secretKey"}, ignoreUnknown = true),修饰类,指定时需要忽略的字段,且在反序列化时忽略 json 中存在的未知字段
- @JsonIgnore,序列化或反序列化时忽略该属性
- @JsonCreator,修饰构造器,指定反序列化时调用的构造器,默认调用无参构造器
- @JsonProperty("uname"),属性值:value(指定反序列化和序列化时该属性对应的名称)、required(该属性是否必须存在 json 中)
- @JsonAlias:指定反序列化时该属性对应的一个或多个别名
- @JsonValue,修饰字段、方法,一个类中最多只能存在一个该注解,表示以该字段值或方法的返回值作为序列化结果
- @JsonEnumDefaultValue,设置默认枚举值(需开启 read_unknown_enum_values_using_default_value=true)
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8"),属性:pattern、timezone、shap(指定序列化后类型),该注解支持 Java 8 中新的日期和时间 API
- @JsonSerialize,修饰字段或者 getter 方法上,用于在序列化时附加自定义的代码,与 @JsonFormat 类似,但是功能更丰富,支持自定义,如 @JsonSerialize(using = ToStringSerializer.class)
- @JsonDeserialize,修饰字段或者 setter 方法上,用于在反序列化时附加自定义的代码,属性:as 指定反序列化后的具体类型(必须是声明类型的子类型),如 @JsonDeserialize(as=LinkedHashSet.class);using 指定反序列化器,如 @JsonDeserialize(using = LocalDateTimeDeserializer.class)
- @JsonNaming,修饰类,用于指定命名的策略,内置的命名策略(PropertyNamingStrategy):SNAKE_CASE、UPPER_CAMEL_CASE、LOWER_CAMEL_CASE(默认)、LOWER_CASE、KEBAB_CASE
在 int 类型枚举字段上标记 @JsonValue 只能用于序列化,对反序列化无效,反序列化时使用的还是枚举的 ordinal() 索引值(相关 Issue (opens new window))
// 时间戳序列化、反序列化为 LocalDateTime
public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
if (value != null) {
long timestamp = value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
gen.writeNumber(timestamp);
}
}
}
public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext)
throws IOException {
long timestamp = p.getValueAsLong();
if (timestamp > 0) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
} else {
return null;
}
}
}
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime timestamp;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// IdWorker 生成主键太大导致 js 精度丢失
@ApiModelProperty(value = "数据 ID", dataType = "java.lang.String")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
2
3
4
# fastjson
所需 jar 包:fastjson.jar
注意:转对象时,该对象的类需提供 setter 方法,转字符串时,根据对象的类提供的 getter 方法
fastjson 默认将 json 格式字符串中的对象结构转换为 JSONObject 对象(implements Map<String, Object>),数组结构转换为 JSONArray对象(implements List<Object>)
JSON 类中的类方法
String toJSONString(Object object)
:对象或集合序列化为 json 格式字符串<T> T parseObject(String text, Class<T> clazz)
:json 格式字符串反序列化为简单类型对象<T> T parseObject(String text, TypeReference<T> type, Feature... features)
:json 格式字符串反序列化为复杂类型对象,如List<Model> models = JSON.parseObject(jsonStr, new TypeReference<List<Model>>() {});
JSONObject parseObject(String text)
:json 格式字符串转为 JSONObject(JSONObject 是 json 字符串与 POJO 对象转换过程中的中间表达类型,实现了 Map 接口,Xxx getXxx(String key)
、xxx getXxxValue(String key)
)<T> List<T> parseArray(String text, Class<T> clazz)
:json 格式字符串反序列化为 List 集合类型对象
JSONArray parseArray(String text)
:json 格式字符串转为 JSONArray(JSONObject 是 JSON 字符串与 List 集合类型对象转换过程中的中间表达类型,实现了 List 接口,Xxx getXxx(int index)
、xxx getXxxValue(int index)
)fastjson 的常用注解
- @JSONField,修饰字段、getter 或 setter 方法,属性:ordinal、name(转 json 格式时的属性名)、alternateNames(反序列化时字段的替代名称)、format、serialize(转 json 格式时是否忽略)、deserialize、serialzeFeatures(指定序列化特性)、parseFeatures(指定反序列化特性)
- @JSONType,修饰类,属性:includes、ignores、naming
SerializerFeature 序列化特性
- QuoteFieldNames,输出 key 时是否使用双引号,默认开启
- SkipTransientField,默认开启
- WriteEnumUsingName,默认开启
- SortField,按字段名称排序后输出,默认开启
- WriteMapNullValue,是否输出值为 null 的字段,默认关闭
- WriteBigDecimalAsPlain,默认关闭
- MapSortField,默认关闭
- DisableCircularReferenceDetect,是否关闭循环/重复引用检测,默认关闭,关闭后,重复引用对象会被
$ref
$[0]
@
$
等引用标识代替;开启后,如果存在循环引用时会导致 StackOverflowError - WriteClassName,默认关闭
- NotWriteDefaultValue,默认关闭
- PrettyFormat,输出结果是否格式化,默认关闭
Feature 反序列化特性
- AutoCloseSource,默认开启
- InternFieldNames,默认开启
- UseBigDecimal,默认开启
- AllowUnQuotedFieldNames,默认开启
- AllowSingleQuotes,默认开启
- AllowArbitraryCommas,默认开启
- SortFeidFastMatch,默认开启
- IgnoreNotMatch,默认开启
- OrderedField,默认关闭
- TrimStringFieldValue
- UseNativeJavaObject,use HashMap instead of JSONObject, ArrayList instead of JSONArray
- DisableCircularReferenceDetect
JSONPath
# Gson
# JSON 的最佳实践
- 在实体类中提供
Map<String, Object> toJson()
方法,Map 中放置需要转换成 json 格式的属性及属性值,在 Controller 中的请求处理方法返回该 Map 对象